package view;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;

import model.Arena;
import model.Macaco;
import model.Player;

import com.sun.opengl.util.FPSAnimator;

public class GLComponent3Dview extends GLCanvas implements GLEventListener {

	private static final long serialVersionUID = 9208387627341708344L;

	public static int larguraArena = 30;
	public static int comprimentoArena = 50;

	private int windowH; // Altura da janela onde estamos desenhando a cena
	private int windowW; // Largura da janela

	/** A��es do BOB */
	private static final int ACTION_WALK = 0;
	private static final int ACTION_STOPED = 1;
	private static final int ACTION_JUMP = 2;
	private static final int ACTION_RUN = 3;
	private static final int ACTION_PUNCH = 4;
	private static final int ACTION_WAVE = 5;
	private static final int ACTION_KICK = 6;
	private static final int ACTION_HADOKEN = 7;
	private static final int ACTION_RUNNING_JUMP = 8;

	/*
	 * Matriz com indices das a��es na lista interna do jogl, ser�
	 * populada no init
	 */
	private int actions[][];
	/* Indices dos outros elementos da cena na lista interna do jogl */
	private int arena, borda, chao, parede, ceu, selecionado;

	private int macaco;

	private GLU glu;
	private FPSAnimator animator;

	private static final float white[] = { 1.0f, 1.0f, 1.0f, 1.0f };

	/* Instru��o a ser impressa na tela, no canto da arena */
	// private String instruct = "Move Bob!";
	private Camera camera; // instancia da c�mera

	/*
	 * A lista abaixo guarda as coordenadas de textura caso elas sejam definidas
	 * em algum .obj, dessa forma se elas n�o mudam (como acontece em geral em
	 * anima��es) as coordenadas de texturas s� precisariam ser definidas
	 * em um dos modelos (o primeiro a ser carregado)
	 */
	static private LinkedList<double[]> text = new LinkedList<double[]>();
	static private boolean textureCoordsDefined = false;

	/**
	 * Construtor da classe GLComponent3Dview. Inicializa o GLcanvas da
	 * aplica��o, a c�mera, o vetor de indices de a��es, o animador e
	 * componente GLU.
	 * 
	 * @param fps
	 *            � o n�mero de frames por segundo que deseja-se manter na
	 *            aplica��o
	 */
	public GLComponent3Dview(int fps) {
		super(getCapabilities());
		addGLEventListener(this);
		glu = new GLU();
		animator = new FPSAnimator(this, fps);
		camera = new Camera(Arena.instanceOf().getSelected().getX() - 10, Arena
				.instanceOf().getSelected().getY(), 0, 10, 10, 0, 10);
		actions = new int[9][70];
	}

	/**
	 * getCapabilities inicializa um novo GLCapabilities e manda ele ser
	 * doubleBuffered e HardwareAccelerated, ao usar esse caps na
	 * inicializa��o do componente fazemos com que ele seja double buffered
	 * e tamb�m acelerado via hardware se poss�vel.
	 */
	private static GLCapabilities getCapabilities() {
		GLCapabilities caps = new GLCapabilities();
		caps.setDoubleBuffered(true);
		caps.setHardwareAccelerated(true);
		return caps;
	}

	/**
	 * init inicializa os componentes necess�rios para o desenho da cena:
	 * Abilita luz e depthTest; pede para que as texturas sejam lidas; l�
	 * todos os quadros de todas as anima��es de a��es de .objs e deixa
	 * eles em uma glList para renderiza��o r�pida depois; desenha os
	 * outros objetos da cena e coloca eles em glLists tamb�m; registra as
	 * classes de tratamento de mouse e teclado para serem chamadas nesses
	 * eventos; inicializa o animador.
	 * 
	 * @param drawable
	 *            � o GLAutoDrawable passado para o init pelo proprio JOGL,
	 *            que � quem chama esse m�todo, ele n�o � chamado pela
	 *            nossa aplica��o.
	 */
	public void init(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		gl.setSwapInterval(1);

		// Cores usadas em alguns materiais abaixo.
		float white[] = { 1.0f, 1.0f, 1.0f, 1.0f };
		float black[] = { 0.0f, 0.0f, 0.0f, 1.0f };

		// Liga a luz e o depthTest
		gl.glEnable(GL.GL_CULL_FACE);
		gl.glEnable(GL.GL_LIGHTING);
		gl.glEnable(GL.GL_LIGHT0);
		gl.glEnable(GL.GL_DEPTH_TEST);

		// Pede para que o TextureHandler carregue as texturas dos arquivos de
		// imagens
		TextureHandler.instanceOf().loadTextures();

		// Carrega do arquivo obj o primeiro objeto, no caso o Bob parado.
		// Nesse arquivo as coordenadas de textura s�o definidas.
		actions[ACTION_STOPED][0] = gl.glGenLists(1);
		gl.glNewList(actions[ACTION_STOPED][0], GL.GL_COMPILE);
		player(gl, "database" + File.separator + "meshes" + File.separator
				+ "player.obj");
		gl.glEndList();

		// Carrega o cliclo de anima��o do andar do Bob, nesses objs n�o
		// precisamos
		// definir as texturas, as texturas definidas no player.obj foram
		// usadas.
		for (int i = 1; i < 41; i++) {
			String name = "walk" + i + ".obj";
			actions[ACTION_WALK][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_WALK][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "walk" + File.separator + name);
			gl.glEndList();
		}

		// Carrega o ciclo do jump para uma glList
		for (int i = 1; i < 41; i++) {
			String name = "jump" + i + ".obj";
			actions[ACTION_JUMP][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_JUMP][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "jump" + File.separator + name);
			gl.glEndList();
		}

		// A segur carrega-se o restante das a��es poss�veis e guarda-se o
		// indice delas
		// na matriz de actions para quando desejarmos desenha-las no m�todo
		// display
		for (int i = 1; i < 61; i++) {
			String name = "run" + i + ".obj";
			actions[ACTION_RUN][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[3][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "run" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "punch" + i + ".obj";
			actions[ACTION_PUNCH][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_PUNCH][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "punch" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "wave" + i + ".obj";
			actions[ACTION_WAVE][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_WAVE][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "wave" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "kick" + i + ".obj";
			actions[ACTION_KICK][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_KICK][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "kick" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "hadoken" + i + ".obj";
			actions[ACTION_HADOKEN][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_HADOKEN][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "hadoken" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "runningJump" + i + ".obj";
			actions[ACTION_RUNNING_JUMP][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_RUNNING_JUMP][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "runningJump" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "walk" + i + ".obj";
			actions[ACTION_WALK][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_WALK][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "walk" + File.separator + name);
			gl.glEndList();
		}

		// Carrega o ciclo do jump para uma glList
		for (int i = 1; i < 41; i++) {
			String name = "jump" + i + ".obj";
			actions[ACTION_JUMP][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_JUMP][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "jump" + File.separator + name);
			gl.glEndList();
		}

		// A segur carrega-se o restante das a��es poss�veis e guarda-se o
		// indice delas
		// na matriz de actions para quando desejarmos desenha-las no m�todo
		// display
		for (int i = 1; i < 61; i++) {
			String name = "run" + i + ".obj";
			actions[ACTION_RUN][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[3][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "run" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "punch" + i + ".obj";
			actions[ACTION_PUNCH][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_PUNCH][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "punch" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "wave" + i + ".obj";
			actions[ACTION_WAVE][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_WAVE][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "wave" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "kick" + i + ".obj";
			actions[ACTION_KICK][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_KICK][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "kick" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "hadoken" + i + ".obj";
			actions[ACTION_HADOKEN][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_HADOKEN][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "hadoken" + File.separator + name);
			gl.glEndList();
		}

		for (int i = 1; i < 41; i++) {
			String name = "runningJump" + i + ".obj";
			actions[ACTION_RUNNING_JUMP][i - 1] = gl.glGenLists(1);
			gl.glNewList(actions[ACTION_RUNNING_JUMP][i - 1], GL.GL_COMPILE);
			player(gl, "database" + File.separator + "meshes" + File.separator
					+ "runningJump" + File.separator + name);
			gl.glEndList();
		}

		// As coordenadas do Bob n�o s�o mais usadas agora, entao limpa-se o
		// text
		text.clear();

		// O restante dos elementos da cena s�o desenhados direto pela
		// aplica��o,
		// com exce��o da parede, que ser� tamb�m carregada de um .obj

		arena = gl.glGenLists(1);
		gl.glNewList(arena, GL.GL_COMPILE);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, white, 0);
		arena(gl);
		gl.glEndList();

		borda = gl.glGenLists(1);
		gl.glNewList(borda, GL.GL_COMPILE);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, white, 0);
		bordaArena(gl);
		gl.glEndList();

		chao = gl.glGenLists(1);
		gl.glNewList(chao, GL.GL_COMPILE);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, white, 0);
		arena(gl);
		gl.glEndList();

		parede = gl.glGenLists(1);
		gl.glNewList(parede, GL.GL_COMPILE);
		player(gl, "database" + File.separator + "meshes" + File.separator
				+ "wall.obj");
		gl.glEndList();

		Macaco macaco1 = new Macaco(10, 10);
		Arena.instanceOf().addMacaco(macaco1);

		macaco = gl.glGenLists(1);
		gl.glNewList(macaco, GL.GL_COMPILE);
		player(gl, "database" + File.separator + "meshes" + File.separator
				+ "Monkey.obj");
		gl.glEndList();

		ceu = gl.glGenLists(1);
		gl.glNewList(ceu, GL.GL_COMPILE);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, white, 0);
		sky(gl);
		gl.glEndList();

		// Selecionado ser� a pequena marca��o que aparece no chao onde o
		// usu�rio clica
		selecionado = gl.glGenLists(1);
		gl.glNewList(selecionado, GL.GL_COMPILE);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, black, 0);
		ring(gl);
		gl.glEndList();

		// gl.glEnable(GL.GL_NORMALIZE);

		// Registramos as classes para tratar eventos de mouse e teclado
		drawable.addMouseListener(MouseInputHandler3D.instanceOf());
		drawable.addMouseMotionListener(MouseInputHandler3D.instanceOf());
		drawable.addMouseWheelListener(MouseInputHandler3D.instanceOf());
		drawable.addKeyListener(KeyboardInputHandler.instanceOf());

		// Passamos a inst�ncia de c�mera sendo usada para que o mouse possa
		// control�-la diretamente.
		MouseInputHandler3D.instanceOf().setCamera(camera);

		animator.start();
	}

	/**
	 * reshape trata eventos de quando a janela muda de tamanho, � tamb�m
	 * chamado no inicio da aplica��o pelo JOGL, para tratar o tamanho
	 * inicial da janela.
	 * 
	 * @param drawable
	 *            � o GLAutoDrawable passado pelo JOGL
	 * @param x
	 *            � a posi��o horizontal do canto da janela
	 * @param y
	 *            � a posi��o vertical do canto da janela
	 * @param width
	 *            � a largura da janela
	 * @param height
	 *            � a altura da janela
	 */
	public void reshape(GLAutoDrawable drawable, int x, int y, int width,
			int height) {
		GL gl = drawable.getGL();

		// Salva a altura e largura atual da tela
		windowH = height;
		windowW = width;
		// Avisa o mouseInputHandler do tamanho, para que ele possa tratar
		// cliques.
		MouseInputHandler3D.instanceOf().setWindowSize(width, height);

		// Janela de visualizacao maior possivel
		gl.glViewport(0, 0, width, height);

		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(camera.getZoom(), (double) width / height, 2.0,
				200.0);
		glu.gluLookAt(camera.getXpos(), camera.getYpos(), camera.getZpos(),
				camera.getXeye(), camera.getYeye(), camera.getZeye(), 0, 0, 1);
		// Arena.instanceOf().getSelected()
		// .setDirection(camera.getXeye(), camera.getYeye());
		gl.glMatrixMode(GL.GL_MODELVIEW);
	}

	/**
	 * display desenha a cena, ele � chamado pelo JOGL sempre que
	 * necess�rio, ele limpa os buffers e redesenha a cena, atrav�s de
	 * chamadas para a glList criada no init.
	 * 
	 * @param drawable
	 *            � o GLAutoDrawable passado pelo JOGL
	 */
	public void display(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();

		// limpa os buffers necess�rios
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT
				| GL.GL_ACCUM_BUFFER_BIT);

		// coloca a c�mera no lugar certo e arruma o tamanho do volume de
		// visualiza��o
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(camera.getZoom(), (double) windowW / windowH, 2.0,
				200.0);
		glu.gluLookAt(camera.getXpos(), camera.getYpos(), camera.getZpos(),
				camera.getXeye(), camera.getYeye(), camera.getZeye(), 0, 0, 1);

		gl.glMatrixMode(GL.GL_MODELVIEW);

		gl.glPushMatrix();

		// Posiciona a luz
		float pos[] = { -1, -1, 15, 0 };
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, pos, 0);

		// desenha a arena:
		/* Arena propria */
		gl.glEnable(GL.GL_TEXTURE_2D);
		TextureHandler.instanceOf().getTextureAt(TextureHandler.TEXTURE_TILE)
				.bind();
		gl.glPushMatrix();
		gl.glCallList(arena);
		gl.glPopMatrix();
		gl.glDisable(GL.GL_TEXTURE_2D);

		/* borda da arena */
		gl.glEnable(GL.GL_TEXTURE_2D);
		TextureHandler.instanceOf().getTextureAt(TextureHandler.TEXTURE_BORDA)
				.bind();
		gl.glPushMatrix();
		gl.glCallList(borda);
		gl.glPopMatrix();
		gl.glDisable(GL.GL_TEXTURE_2D);

		/* chao do lugar */
		gl.glEnable(GL.GL_TEXTURE_2D);
		TextureHandler.instanceOf().getTextureAt(TextureHandler.TEXTURE_CHAO)
				.bind();
		gl.glPushMatrix();
		gl.glTranslated(-comprimentoArena, -larguraArena, 0);
		gl.glScaled(2.5, 2.5, 1);
		gl.glTranslated(5, 3, -0.9);
		gl.glCallList(chao);
		gl.glPopMatrix();
		gl.glDisable(GL.GL_TEXTURE_2D);

		/* parede em volta */
		gl.glEnable(GL.GL_TEXTURE_2D);
		TextureHandler.instanceOf().getTextureAt(TextureHandler.TEXTURE_PAREDE)
				.bind();
		gl.glPushMatrix();
		gl.glCallList(parede);
		gl.glPopMatrix();
		gl.glDisable(GL.GL_TEXTURE_2D);

		/* ceu acima */
		gl.glEnable(GL.GL_TEXTURE_2D);
		TextureHandler.instanceOf().getTextureAt(TextureHandler.TEXTURE_CEU)
				.bind();
		gl.glPushMatrix();
		gl.glCallList(ceu);
		gl.glPopMatrix();
		gl.glDisable(GL.GL_TEXTURE_2D);

		// Desenha o(s) Bob(s)
		double fatorX, fatorY;

		// Pega a posicao do Bob e poe o foco da camera nele
		fatorX = Arena.instanceOf().getSelected().getX();
		fatorY = Arena.instanceOf().getSelected().getY();

		camera.lookTo(fatorX, fatorY, 1.3);
		for (int i = 0; i < Arena.instanceOf().getBobs().size(); i++) {
			rotateBob(gl, Arena.instanceOf().getBobs().get(i));
		}

		desenhaMacaco(gl, Arena.instanceOf().getMacacos().getFirst(), Arena
				.instanceOf().getSelected());

		// Codigo abaixo � usado para escrever algo na tela em 2D, no momento
		// n�o � usado.
		/*
		 * GLUT glut = new GLUT(); gl.glRasterPos3d(0, 0, 0); if(instruct !=
		 * null) glut.glutBitmapString(GLUT.BITMAP_HELVETICA_12, instruct);
		 */

		// Desenha a aura do ultimo click selecionado
		if (Arena.instanceOf().isCliked()) {
			fatorX = Arena.instanceOf().getXClick();
			fatorY = Arena.instanceOf().getYClick();
			gl.glPushMatrix();
			gl.glTranslated(fatorX, fatorY, 0.01f);
			gl.glCallList(selecionado);
			gl.glPopMatrix();
		}

		gl.glPopMatrix();
	}

	private void rotateBob(GL gl, Player bob) {
		double fatorX, fatorY, angle, dirX, dirY, dist;
		fatorX = bob.getX();
		fatorY = bob.getY();
		// Pega direcao do Bob, transforma o vetor em unitario e calcula a
		// rotacao do Bob
		dirX = bob.getDirX();
		dirY = bob.getDirY();
		dist = Math.sqrt(dirX * dirX + dirY * dirY);
		dirX = dirX / dist;
		dirY = dirY / dist;
		angle = Math.acos(dirX);
		if (dirY < 0)
			angle = -angle;
		// Bob � desenhado olhando para y negativo, nao x positivo, por isso
		// o fator de 90
		angle = Math.toDegrees(angle) + 90;

		// Aplica uma textura ao Bob, translada e rotacionao a matriz de
		// desenho de acordo com a posicao e rotacao desse Bob
		gl.glEnable(GL.GL_TEXTURE_2D);
		TextureHandler.instanceOf().getTextureAt(1).bind();
		gl.glPushMatrix();
		gl.glTranslated(fatorX, fatorY, 0f);
		gl.glRotated(angle, 0, 0, 1);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, white, 0);
		// chama a glList da a��o que esse Bob est� executando, no quadro
		// em
		// que ele est� no momento.
		gl.glCallList(actions[getIndex(bob.getCurrentAction())][bob.getStep()]);
		gl.glPopMatrix();
		gl.glDisable(GL.GL_TEXTURE_2D);
	}

	private void desenhaMacaco(GL gl, Macaco macaco, Player bob) {
		double fatorX, fatorY, angle, dirX, dirY, dist;
		fatorX = macaco.getX();
		fatorY = macaco.getY();
		// Pega direcao do Bob, transforma o vetor em unitario e calcula a
		// rotacao do Bob
		dirX = bob.getX();
		dirY = bob.getY();
		dist = Math.sqrt(dirX * dirX + dirY * dirY);
		dirX = dirX / dist;
		dirY = dirY / dist;
		angle = Math.acos(dirX);
		if (dirY < 0)
			angle = -angle;
		// Bob � desenhado olhando para y negativo, nao x positivo, por isso
		// o fator de 90
		angle = Math.toDegrees(angle) + 90;

		// Aplica uma textura ao Bob, translada e rotacionao a matriz de
		// desenho de acordo com a posicao e rotacao desse Bob
		gl.glEnable(GL.GL_TEXTURE_2D);
		TextureHandler.instanceOf().getTextureAt(1).bind();
		gl.glPushMatrix();
		gl.glTranslated(fatorX, fatorY, 0f);
		gl.glRotated(angle, 0, 0, 1);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, white, 0);
		// chama a glList da a��o que esse Bob est� executando, no quadro
		// em
		// que ele est� no momento.
		gl.glCallList(this.macaco);
		gl.glPopMatrix();
		gl.glDisable(GL.GL_TEXTURE_2D);
	}

	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
			boolean deviceChanged) {
	}

	/**
	 * player � respons�vel por carregar um arquivo .obj e desenha-lo, ele
	 * � chamado para popular uma glList com os objetos carregados atrav�s
	 * de .objs
	 * 
	 * @param gl
	 *            � o GL do contexto onde a glList esta sendo criada, usado
	 *            para desenhar o objeto
	 * @param path
	 *            � o caminho e o nome do arquivo .obj de onde ele deve ler o
	 *            objeto
	 */
	public static void player(GL gl, String path) {
		GLU tool = new GLU();
		tool.gluQuadricTexture(tool.gluNewQuadric(), true);

		try {
			// Ferramentas para ler o arquivo de entrada
			FileInputStream stream = new FileInputStream(path);
			BufferedReader in = new BufferedReader(
					new InputStreamReader(stream));

			String s, splited[];
			LinkedList<double[]> vertex = new LinkedList<double[]>();
			LinkedList<double[]> normal = new LinkedList<double[]>();
			boolean normalsDefined = false;
			boolean texCoordDefinedHere = false;

			while (true) {
				s = in.readLine();
				if (s == null)
					break;
				splited = s.split(" ");

				// Defini��o de v�rtice na linha
				if (splited[0].compareTo("v") == 0) {
					if (splited.length != 4) {
						System.out.println("Obj file with unexpected format");
						break;
					}
					double point[] = new double[3];
					point[0] = Double.parseDouble(splited[1]); // x coordinate
					point[1] = Double.parseDouble(splited[2]); // y coordinate
					point[2] = Double.parseDouble(splited[3]); // z coordinate
					vertex.addLast(point);
				}
				// Definicao de normal de v�rtice na linha
				else if (splited[0].compareTo("vn") == 0) {
					if (splited.length != 4) {
						System.out.println("Obj file with unexpected format");
						break;
					}
					double point[] = new double[3];
					point[0] = Double.parseDouble(splited[1]); // x coordinate
					point[1] = Double.parseDouble(splited[2]); // y coordinate
					point[2] = Double.parseDouble(splited[3]); // z coordinate
					normal.addLast(point);
					normalsDefined = true;
				}
				// Definicao de coordenada de textura na linha
				else if (splited[0].compareTo("vt") == 0) {
					if (splited.length != 4) {
						System.out.println("Obj file with unexpected format");
						break;
					}
					double point[] = new double[3];
					point[0] = Double.parseDouble(splited[1]); // x coordinate
					point[1] = Double.parseDouble(splited[2]); // y coordinate
					point[2] = Double.parseDouble(splited[3]); // z coordinate
					text.addLast(point);
					textureCoordsDefined = true;
					// Se as coordenadas foram definidas nesse arquivo
					// precisarei fazer o parsing delas, senao usarei as ja
					// guardadas.
					texCoordDefinedHere = true;
				}
				// Defini��o de face na linha
				else if (splited[0].compareTo("f") == 0) {
					if (splited.length < 4) {
						System.out.println("Obj file with unexpected format");
						break;
					}
					/*
					 * Se as texturas foram definidas em outro arquivo,
					 * guarda-se o indice para pegar a coordenada do vetor ja
					 * definido.
					 */
					int texCoordIndex = 1;

					gl.glBegin(GL.GL_POLYGON);
					if (normalsDefined && !textureCoordsDefined) {
						for (int i = 1; i < splited.length; i++) {
							String split2[];
							split2 = splited[i].split("//");
							double[] v = vertex
									.get(Integer.parseInt(split2[0]) - 1);
							double[] vn = normal.get(Integer
									.parseInt(split2[1]) - 1);
							gl.glVertex3d(v[0], v[1], v[2]);
							gl.glNormal3d(vn[0], vn[1], vn[2]);
						}
					}
					/*
					 * Se as coordenadas de textura e normais foram definidas
					 * nesse arquivo
					 */
					else if (normalsDefined && textureCoordsDefined
							&& texCoordDefinedHere) {
						for (int i = 1; i < splited.length; i++) {
							String split2[];
							split2 = splited[i].split("/");
							double[] v = vertex
									.get(Integer.parseInt(split2[0]) - 1);
							double[] vt = text
									.get(Integer.parseInt(split2[1]) - 1);
							double[] vn = normal.get(Integer
									.parseInt(split2[2]) - 1);
							gl.glTexCoord2d(vt[0], vt[1]);
							gl.glNormal3d(vn[0], vn[1], vn[2]);
							gl.glVertex3d(v[0], v[1], v[2]);
						}
					}
					/*
					 * Se as normais foram definidas aqui e as coordenadas de
					 * textura foram definidas em lugar.
					 */
					else if (normalsDefined && textureCoordsDefined
							&& !texCoordDefinedHere) {
						for (int i = 1; i < splited.length; i++) {
							String split2[];
							split2 = splited[i].split("//");
							double[] v = vertex
									.get(Integer.parseInt(split2[0]) - 1);
							double[] vt = text.get(texCoordIndex);
							texCoordIndex++;
							double[] vn = normal.get(Integer
									.parseInt(split2[1]) - 1);
							gl.glTexCoord2d(vt[0], vt[1]);
							gl.glVertex3d(v[0], v[1], v[2]);
							gl.glNormal3d(vn[0], vn[1], vn[2]);
						}
					} else if (!normalsDefined && !textureCoordsDefined) {
						for (int i = 1; i < splited.length; i++) {
							double[] v = vertex.get(Integer
									.parseInt(splited[i]) - 1);
							gl.glVertex3d(v[0], v[1], v[2]);
						}
					}
					gl.glEnd();
				}
			}
			in.close();
		} catch (Exception e) {
			System.err.println("File input error");
			e.printStackTrace();
		}
	}

	/**
	 * arena desenha a parte da arena onde o Bob anda, esse m�todo � chamado
	 * no init para criar a arena na glList
	 */
	public static void arena(GL gl) {
		/* desenha Arena (dividida em tiles de 2x2) */
		for (int i = 0; i < larguraArena; i = i + 2) {
			for (int j = 0; j < comprimentoArena; j = j + 2) {
				gl.glBegin(GL.GL_QUADS);
				gl.glNormal3d(0, 0, 1);
				gl.glTexCoord2f(0.0f, 1.0f);
				gl.glVertex3f(j, i + 2, 0);
				gl.glTexCoord2f(0.0f, 0.0f);
				gl.glVertex3f(j, i, 0);
				gl.glTexCoord2f(1.0f, 0.0f);
				gl.glVertex3f(j + 2, i, 0);
				gl.glTexCoord2f(1.0f, 1.0f);
				gl.glVertex3f(j + 2, i + 2, 0);
				gl.glEnd();
			}
		}
	}

	/**
	 * bordaArena desenha a parte da arena onde o Bob anda, esse m�todo �
	 * chamado no init para criar a borda na glList, as coordenadas de textura
	 * s�o definidas tamb�m.
	 */
	public static void bordaArena(GL gl) {
		/* desenha a borda da arena (dividida em tiles de 1x2) */
		/* Bordas horizontais */
		for (int i = -1; i <= larguraArena; i = i + larguraArena + 1) {
			for (int j = 0; j < comprimentoArena; j = j + 2) {
				gl.glBegin(GL.GL_QUADS);
				gl.glNormal3d(0, 0, 1);
				gl.glTexCoord2f(0.0f, 1.0f);
				gl.glVertex3f(j, i + 1, 0);
				gl.glTexCoord2f(0.0f, 0.0f);
				gl.glVertex3f(j, i, 0);
				gl.glTexCoord2f(1.0f, 0.0f);
				gl.glVertex3f(j + 2, i, 0);
				gl.glTexCoord2f(1.0f, 1.0f);
				gl.glVertex3f(j + 2, i + 1, 0);
				gl.glEnd();
			}
		}
		/* Bordas verticais */
		for (int i = 0; i < larguraArena; i = i + 2) {
			for (int j = -1; j <= comprimentoArena; j = j + comprimentoArena
					+ 1) {
				gl.glBegin(GL.GL_QUADS);
				gl.glNormal3d(0, 0, 1);
				gl.glTexCoord2f(1.0f, 1.0f);
				gl.glVertex3f(j, i + 2, 0);
				gl.glTexCoord2f(0.0f, 1.0f);
				gl.glVertex3f(j, i, 0);
				gl.glTexCoord2f(0.0f, 0.0f);
				gl.glVertex3f(j + 1, i, 0);
				gl.glTexCoord2f(1.0f, 0.0f);
				gl.glVertex3f(j + 1, i + 2, 0);
				gl.glEnd();
			}
		}
		/* As 4 Quinas. */
		gl.glBegin(GL.GL_QUADS); /* Inferior esquerda */
		gl.glNormal3d(0, 0, 1);
		gl.glTexCoord2f(1, 1);
		gl.glVertex3f(-1, 0, 0);
		gl.glTexCoord2f(1 / 2, 1);
		gl.glVertex3f(-1, -1, 0);
		gl.glTexCoord2f(1, 1);
		gl.glVertex3f(0, -1, 0);
		gl.glTexCoord2f(1, 0);
		gl.glVertex3f(0, 0, 0);
		gl.glEnd();
		gl.glBegin(GL.GL_QUADS); /* Inferior direita */
		gl.glNormal3d(0, 0, 1);
		gl.glTexCoord2f(1, 1);
		gl.glVertex3f(comprimentoArena, -1, 0);
		gl.glTexCoord2f(1 / 2, 1);
		gl.glVertex3f(comprimentoArena + 1, -1, 0);
		gl.glTexCoord2f(1, 1);
		gl.glVertex3f(comprimentoArena + 1, 0, 0);
		gl.glTexCoord2f(1, 0);
		gl.glVertex3f(comprimentoArena, 0, 0);
		gl.glEnd();
		gl.glBegin(GL.GL_QUADS); /* Superior esquerda */
		gl.glNormal3d(0, 0, 1);
		gl.glTexCoord2f(1, 1);
		gl.glVertex3f(0, larguraArena + 1, 0);
		gl.glTexCoord2f(1 / 2, 1);
		gl.glVertex3f(-1, larguraArena + 1, 0);
		gl.glTexCoord2f(1, 1);
		gl.glVertex3f(-1, larguraArena, 0);
		gl.glTexCoord2f(1, 0);
		gl.glVertex3f(0, larguraArena, 0);
		gl.glEnd();
		gl.glBegin(GL.GL_QUADS); /* Superior direita */
		gl.glNormal3d(0, 0, 1);
		gl.glTexCoord2f(1, 1);
		gl.glVertex3f(comprimentoArena, larguraArena + 1, 0);
		gl.glTexCoord2f(1, 0);
		gl.glVertex3f(comprimentoArena, larguraArena, 0);
		gl.glTexCoord2f(1, 1);
		gl.glVertex3f(comprimentoArena + 1, larguraArena, 0);
		gl.glTexCoord2f(1 / 2, 1);
		gl.glVertex3f(comprimentoArena + 1, larguraArena + 1, 0);
		gl.glEnd();

		/* Agora as bordas abaixo da arena, que a contornam */
		for (int j = -1; j < comprimentoArena; j = j + 2) { /* Parte da frente */
			gl.glBegin(GL.GL_QUADS);
			gl.glNormal3d(0, -1, 0);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3f(j, -1, 0);
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3f(j, -1, -1);
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3f(j + 2, -1, -1);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3f(j + 2, -1, 0);
			gl.glEnd();
		}
		for (int j = -1; j < comprimentoArena; j = j + 2) { /* Parte de traz */
			gl.glBegin(GL.GL_QUADS);
			gl.glNormal3d(0, 1, 0);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3f(j + 2, larguraArena + 1, 0);
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3f(j + 2, larguraArena + 1, -1);
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3f(j, larguraArena + 1, -1);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3f(j, larguraArena + 1, 0);
			gl.glEnd();
		}
		for (int i = -1; i < larguraArena; i = i + 2) { /* Parte da esquerda */
			gl.glBegin(GL.GL_QUADS);
			gl.glNormal3d(-1, 0, 0);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3f(-1, i + 2, 0);
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3f(-1, i + 2, -1);
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3f(-1, i, -1);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3f(-1, i, 0);
			gl.glEnd();
		}
		for (int i = -1; i < larguraArena; i = i + 2) { /* Parte da direita */
			gl.glBegin(GL.GL_QUADS);
			gl.glNormal3d(1, 0, 0);
			gl.glTexCoord2f(0.0f, 1.0f);
			gl.glVertex3f(comprimentoArena + 1, i, 0);
			gl.glTexCoord2f(0.0f, 0.0f);
			gl.glVertex3f(comprimentoArena + 1, i, -1);
			gl.glTexCoord2f(1.0f, 0.0f);
			gl.glVertex3f(comprimentoArena + 1, i + 2, -1);
			gl.glTexCoord2f(1.0f, 1.0f);
			gl.glVertex3f(comprimentoArena + 1, i + 2, 0);
			gl.glEnd();
		}
	}

	/**
	 * sky desenha o c�u da arena onde o Bob anda
	 */
	public static void sky(GL gl) {
		gl.glBegin(GL.GL_QUADS);
		gl.glTexCoord2f(0.0f, 1.0f);
		gl.glVertex3f(-65, -50, 21);
		gl.glTexCoord2f(0.0f, 0.0f);
		gl.glVertex3f(-65, 80, 21);
		gl.glTexCoord2f(1.0f, 0.0f);
		gl.glVertex3f(113, 80, 21);
		gl.glTexCoord2f(1.0f, 1.0f);
		gl.glVertex3f(113, -50, 21);
		gl.glEnd();
	}

	public static void ring(GL gl) {
		GLU tool = new GLU();
		tool.gluDisk(tool.gluNewQuadric(), 0.2, 0.4, 20, 2);
	}

	/**
	 * setInstruct muda a instru��o em texto que pode ser desenhada pelo
	 * JOGL.
	 * 
	 * @param newInstruct
	 *            � a string que passar� a ser desenhada na tela caso a
	 *            instrucao esteja sendo desenhada.
	 */
	public void setInstruct(String newInstruct) {
		// instruct = newInstruct;
	}

	/**
	 * getIndex responde o indice de uma a��o na matriz de indices da glList
	 * 
	 * @param actionName
	 *            � uma string com o nome da a��o
	 * @return a linha daquela a��o na matriz de indices
	 */
	public int getIndex(String actionName) {
		if (actionName.equals("Move")) {
			return 0;
		} else if (actionName.equals("Jump")) {
			return 2;
		} else if (actionName.equals("Run")) {
			return 3;
		} else if (actionName.equals("Teleport")) {
			return 5;
		} else if (actionName.equals("Punch")) {
			return 4;
		} else if (actionName.equals("Wave")) {
			return 5;
		} else if (actionName.equals("Kick")) {
			return 6;
		} else if (actionName.equals("Hadoken")) {
			return 7;
		} else if (actionName.equals("RunningJump")) {
			return 8;
		} else if (actionName.equals("Clone")) {
			return 5;
		} else {
			return 1;
		}
	}

	public static boolean isValidPosition(double x, double y) {

		if (x > comprimentoArena + 1 || x <= -1 || y >= larguraArena + 1
				|| y <= -1) {
			return false;
		}
		return true;
	}

	public Camera getCamera() {
		return camera;
	}
}
